﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

/**
 * Calculates all the holidays in a given year, according to current Icelandic laws.
 * and allow us to check if a certain date is a holiday.
 * 
 * Each holiday in this class indicates the start of the day.
 * 
 * @issue INN-652
 * @author Hugi Þórðarson
 * @reviewedby Logi Helgu, Jun 24, 2009 
 * @converted to C# Ingi Gauti Ragnarsson
 */
namespace Is.Net {

	public class Holiday {

		/**
		 * The year to get holidays for
		 */
		private int _year;

		/**
		 * Constructs a new holidays calculator.
		 *
		 * @param newYear The year to calculate holidays for.
		 */
		public Holiday(int newYear) {
			Year = newYear;
		}

		/**
		 * The year to calculate holidays for.
		 * @param newYear The year to calculate holidays for.
		 */
		public int Year {
			get { return _year; }
			set { _year = value; }
		}


		/**
		 * All Icelandic holiday dates, partial and complete.
		 * Note that some of these holidays do not last the whole day.
		 * To get the days that last a whole day use fullHoldays.
		 * To get the days that last part of the day use partialHolidays.
		 */
		public List<DateTime> AllHolidays() {
			List<DateTime> a = new List<DateTime>();

			a.Add(NewYearsDay());
			a.Add(MaundyThursday());
			a.Add(GoodFriday());
			a.Add(EasterDay());
			a.Add(EasterMonday());
			a.Add(FirstDayOfSummer());
			a.Add(FirstOfMay());
			a.Add(AscensionDay());
			a.Add(WhiteMonday());
			a.Add(SeventeenthOfJune());
			a.Add(TrademensDay());
			a.Add(ChristmasEve());
			a.Add(ChristmasDay());
			a.Add(BoxingDay());
			a.Add(NewYearsEve());

			return a;
		}

		/**
		* Icelandic full-day holidays.
		*/
		public List<DateTime> FullHolidays() {
			List<DateTime> a = new List<DateTime>();

			a.Add(NewYearsDay());
			a.Add(MaundyThursday());
			a.Add(GoodFriday());
			a.Add(EasterDay());
			a.Add(EasterMonday());
			a.Add(FirstDayOfSummer());
			a.Add(FirstOfMay());
			a.Add(AscensionDay());
			a.Add(WhiteMonday());
			a.Add(SeventeenthOfJune());
			a.Add(TrademensDay());
			a.Add(ChristmasDay());
			a.Add(BoxingDay());

			return a;
		}

		/**
		* Partial Icelandic holidays. (holiday after noon).
		*/
		public List<DateTime> PartialHolidays() {
			List<DateTime> a = new List<DateTime>();

			a.Add(ChristmasEve());
			a.Add(NewYearsEve());

			return a;
		}

		/**
		 * Returns true if the date is a holiday
		 */
		public bool IsHoliday(DateTime date) {
			List<DateTime> holidays = AllHolidays();
			return holidays.Contains(date.Date);
		}

		/**
		 * New Year's Day (Icelandic: Nýársdagur)
		 */
		public DateTime NewYearsDay() {
			return new DateTime(Year, 1, 1);
		}

		/**
		 * Maundy Thursday (Icelandic: Skírdagur)
		 * Thursday before Easter Sunday.
		 */
		public DateTime MaundyThursday() {
			return EasterDay().AddDays(-3);
		}

		/**
		 * Good Friday (Icelandic: Föstudagurinn langi)
		 * Friday before Easter Sunday.
		 */
		public DateTime GoodFriday() {
			return EasterDay().AddDays(-2);
		}

		/**
		 * Easter Day (Icelandic: Páskadagur)
		 *
		 * Borrowed from: http://www.smart.net/~mmontes/nature1876.html
		 *
		 * Holds for any year in the Gregorian Calendar, which (of course) means years including and after 1583.
		 */
		public DateTime EasterDay() {
			int a = Year % 19;
			int b = Year / 100;
			int c = Year % 100;
			int d = b / 4;
			int e = b % 4;
			int f = (b + 8) / 25;
			int g = (b - f + 1) / 3;
			int h = (19 * a + b - d - g + 15) % 30;
			int i = c / 4;
			int k = c % 4;
			int l = (32 + 2 * e + 2 * i - h - k) % 7;
			int m = (a + 11 * h + 22 * l) / 451;
			int month = ((h + l - 7 * m + 114) / 31);
			int p = (h + l - 7 * m + 114) % 31;
			int day = p + 1;

			return new DateTime(Year, month, day);
		}

		/**
		 * Easter Monday (Icelandic: Annar í páskum)
		 * Monday following Easter Sunday
		 */
		public DateTime EasterMonday() {
			return EasterDay().AddDays(1);
		}

		/**
		 * First day of summer (Icelandic: Sumardagurinn fyrsti)
		 * Thursday during the period 19 to 25 April
		 */
		public DateTime FirstDayOfSummer() {
			DateTime dt = new DateTime(Year, 4, 19);
			for (int i = 0; i < 7; i++) {
				if (dt.AddDays(i).DayOfWeek == DayOfWeek.Thursday) return dt.AddDays(i);
			}
			return DateTime.MinValue;
		}

		/**
		 * Labour day (Icelandic: Verkalýðdagurinn (1. maí))
		 */
		public DateTime FirstOfMay() {
			return new DateTime(Year, 5, 1);
		}

		/**
		 * Ascension day (Icelandic: Uppstigningardagur).
		 * Holy Thursday, six weeks after Maundy Thursday.
		 */
		public DateTime AscensionDay() {
			return MaundyThursday().AddDays(42);
		}


		/**
		 * White Monday (Icelandic: Annar í hvítasunnu).
		 * Monday following White Sunday, seven weeks after Easter.
		 */
		public DateTime WhiteMonday() {
			return EasterMonday().AddDays(49);
		}

		/**
		 * Seventeenth of June (Icelandic: Þjóðhátíðardagurinn( 17. júní)).
		 * Icelandic national holiday.
		 */
		public DateTime SeventeenthOfJune() {
			return new DateTime(Year, 6, 17);
		}

		/**
		 * Trademen's Day (Icelandic: Frídagur verslunarmanna).
		 * First Monday in August.
		 */
		public DateTime TrademensDay() {
			DateTime dt = new DateTime(Year, 8, 1);
			for (int i = 0; i < 7; i++) {
				if (dt.AddDays(i).DayOfWeek == DayOfWeek.Monday) return dt.AddDays(i);
			}
			return DateTime.MinValue;
		}

		/**
		 * Christmas Eve (Icelandic: Aðfangadagur).
		 * Afternoon only.
		 */
		public DateTime ChristmasEve() {
			return new DateTime(Year, 12, 24);
		}

		/**
		 * Christmas Day (Icelandic: Jóladagur).
		 */
		public DateTime ChristmasDay() {
			return new DateTime(Year, 12, 25);
		}

		/**
		 * Boxing Day (Icelandic: Annar í jólum).
		 */
		public DateTime BoxingDay() {
			return new DateTime(Year, 12, 26);
		}

		/**
		 * New Year's Eve (Icelandic: Gamlársdagur).
		 * Afternoon only.
		 */
		public DateTime NewYearsEve() {
			return new DateTime(Year, 12, 31);
		}
	}

}
